import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.Control;
import javax.sound.sampled.Line;

import java.io.InputStream;
import java.io.IOException;

import java.lang.Math;



public class MorseDataLine implements TargetDataLine
{
    protected InputStream input;
    protected AudioFormat format;

    protected byte[]      audio_buffer;
    protected int         audio_buffer_size;
    protected int         audio_buffer_in;
    protected int         audio_buffer_out;
    
    protected int         speed;
    protected int         eff_speed;


    public MorseDataLine(InputStream in)
    {
	// Store input stream:
	this.input = in;

	// Store current audio format.
	this.format = new AudioFormat((float)44100.0, 16, 1, true, true);

	this.speed = 25;
	this.eff_speed = 20;

	// allocate buffer:
	int min_buff_size = ((int)Math.ceil(44.1*1200/this.eff_speed))*2;
	this.audio_buffer_size = 8*min_buff_size;
	this.audio_buffer = new byte[this.audio_buffer_size];

	
	this.audio_buffer_in = 0;
	this.audio_buffer_out = 0;
    }


    public void open()
    {
    }


    public void open(AudioFormat format)
    {
    }


    public void open(AudioFormat format, int bufferSize)
    {
    }


    public int read(byte[] buf, int offset, int size)
    {
	int nbytes_send = 0;

	if(size > this.available()) {
	    size = this.available();
	}

	if(this.audio_buffer_out+size > this.audio_buffer_size)
	    {
		nbytes_send = this.audio_buffer_size-this.audio_buffer_out;

		System.arraycopy(this.audio_buffer, this.audio_buffer_out, 
				 buf, offset, 
				 nbytes_send);

		size = size - nbytes_send;
		this.audio_buffer_out = 0;
	    }
	
	System.arraycopy(this.audio_buffer, this.audio_buffer_out, 
			 buf, offset, 
			 size);

	nbytes_send += size;
	
	return nbytes_send;
    }


    public float getLevel()
    {
	return (float) 1.0;
    }

    
    public long getMicrosecondPosition()
    {
	return (long) 0;
    }


    public long getLongFramePosition()
    {
	return (long) 0;
    }


    public int getFramePosition()
    {
	return (int) 0;
    }


    public int available()
    {
	// Calc num. bytes:
	if(this.audio_buffer_in >= this.audio_buffer_out) {
	    return this.audio_buffer_in - this.audio_buffer_out;
	}

	return (this.audio_buffer_size-this.audio_buffer_out) + this.audio_buffer_in;
    }


    public int getBufferSize()
    {
	return this.audio_buffer_size;
    }


    public AudioFormat getFormat()
    {
	return new AudioFormat((float)44100, 16, 1, true, true);
    }


    public boolean isActive()
    {
	return false;
    }


    public boolean isRunning()
    {
	return false;
    }

    
    public void stop()
    {
    }


    public void start()
    {
    }


    public void flush()
    {
    }


    public void drain()
    {
    }


    
    public void removeLineListener(LineListener listener)
    {
    }


    public void addLineListener(LineListener listener)
    {
    }


    public Control getControl(Control.Type type)
    {
	return null;
    }


    public boolean isControlSupported(Control.Type type)
    {
	return false;
    }


    public Control[] getControls()
    {
	return new Control[0];
    }


    public boolean isOpen()
    {
	return false;
    }

    
    public void close()
    {
	// also close input stream...
	try {
	    this.input.close();
	} catch (IOException err) {
	    // OOps...
	}
    }


    public Line.Info getLineInfo()
    {
	return null;
    }
};
